extends CharacterBody3D
class_name Knight

const ACCELERATION = 15
const MOVE_SPEED = 5.0
const LOOK_SPEED = 0.5
const JUMP_VELOCITY = 10
const DIRECTION_CHANGE_SPEED = 5
const MOVEMENT_ANIMATION_TRANSITION_SPEED = 0.15
const CURRENT_STATE_PLAYBACK_PATH = "parameters/SM_Movement/playback"
const DODGE_VELOCITY = 15

@onready var spring_arm_3d: SpringArm3D = $SpringArm3D
@onready var animation_tree: AnimationTree = $AnimationTree
@onready var rig: Node3D = $Rig
@onready var interaction_handler: InteractionHandler = $InteractionHandler


enum MovementMode {
	UNFOCUSED,
	FOCUSED,
}

var current_input: Vector2
var current_velocity: Vector2
var mouse_captured: bool = false

var movement_mode: MovementMode:
	set(value):
		animation_tree.set("parameters/SM_Movement/conditions/focused_movement", value == MovementMode.FOCUSED)
		animation_tree.set("parameters/SM_Movement/conditions/unfocused_movement", value == MovementMode.UNFOCUSED)

		movement_mode = value

var jump_queued: bool = false
var falling: bool = false
var is_dodging: bool = false

func _ready() -> void:
	movement_mode = MovementMode.UNFOCUSED
	
func _process(delta: float) -> void:
	# 这里实现的目标是？
	var velocityDelta = (current_input - current_velocity)  # 速度增量
	if velocityDelta.length() > MOVEMENT_ANIMATION_TRANSITION_SPEED:
		velocityDelta = velocityDelta.normalized() * MOVEMENT_ANIMATION_TRANSITION_SPEED
	current_velocity += velocityDelta

	var animation_velocity = current_velocity 
	animation_velocity.y *= -1
	if movement_mode == MovementMode.UNFOCUSED:
		# 使用absf岂不是始终固定在右侧动画？
		var velocity_value = maxf(absf(animation_velocity.x), absf(animation_velocity.y))
		animation_tree.set("parameters/SM_Movement/BS_UnfocusMovement/blend_position", velocity_value)
	else:
		animation_tree.set("parameters/SM_Movement/BS_FocusMovement/blend_position", animation_velocity)

func _physics_process(delta: float) -> void:
	# 获取输入向量, 左: (-1, 0),  右: (1, 0), 上: (0, -1), 下: (0, 1)。2D下Y轴正方向向下
	#  如果是斜方向，x、y均会有值，并且不会为1（或-1）
	if not is_dodging:
		current_input = Input.get_vector("move_left", "move_right", "move_up", "move_down") # 获取输入向量
		# 绕指定轴旋转，将输入向量转为角色(即相机)的朝向
		var dir = Vector3(current_input.x, 0, current_input.y).rotated(Vector3.UP, spring_arm_3d.rotation.y)
		var velocity_y = velocity.y
		velocity = lerp(velocity, dir * MOVE_SPEED, delta * ACCELERATION)
		velocity.y  = velocity_y

	handle_rotation(delta)
	handle_jumping(delta)
	
	move_and_slide()

func _input(event: InputEvent) -> void:
	if Input.is_action_just_pressed("focus"):
		movement_mode = MovementMode.FOCUSED if movement_mode == MovementMode.UNFOCUSED else MovementMode.UNFOCUSED
	if Input.is_action_just_pressed("jump") and is_on_floor():
		begin_jump()
	if Input.is_action_just_pressed("dodge"):
		do_dodge()

# 鼠标左右上下移动
func _unhandled_input(event: InputEvent) -> void:
	if !mouse_captured and Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
		Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
		mouse_captured = true
	elif mouse_captured and Input.is_key_pressed(KEY_ESCAPE):
		Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
		mouse_captured = false

	if mouse_captured and event is InputEventMouseMotion:
		rotate_look(spring_arm_3d, event.relative)

func handle_rotation(delta: float) -> void:
	if is_dodging:
		return

	if movement_mode == MovementMode.UNFOCUSED and velocity.length() > 0.01:
		# 如果速度大于0，旋转角色朝向到移动的方向，-atan2(velocity.x, -velocity.z)[To-Do]数学原理？
		if velocity.z != 0:
			rig.rotation.y = lerp_angle(rig.rotation.y, -atan2(velocity.x, -velocity.z), delta * DIRECTION_CHANGE_SPEED)
	elif movement_mode == MovementMode.FOCUSED and velocity.length() > 0.01:
		rig.rotation.y = lerp_angle(rig.rotation.y, spring_arm_3d.rotation.y, delta * DIRECTION_CHANGE_SPEED)

func handle_jumping(delta: float) ->void:
	velocity += get_gravity() * delta

	if not is_on_floor():
		jump_queued = false
		if !falling:
			falling = true
			travel_to_state("Jump_Idle")
	elif falling:
		falling = false
		travel_to_state("Jump_Land") 

	if jump_queued:
		velocity.y = JUMP_VELOCITY
		jump_queued = false
		falling = false

#func handle_jumping(delta: float) -> void:
	## 当角色在地板上时，move_and_slide 根据重力调整在y轴上的速度
	## 上升过程中播放Jump_Start动画，从最高点下落过程中播放Jump_Idle，落地后播放Jump_Land
	#if jump_state == JumpState.UP:
		#if is_on_floor():
			## 地面， 起跳
			#travel_to_state("Jump_Start")
			#velocity.y = JUMP_VELOCITY
		#else:
			## 不在地面，上升 
			#velocity += get_gravity()
			#if velocity.y < 0:
				## 到达最高点
				#travel_to_state("Jump_Idle")
				#jump_state = JumpState.DOWN
	#elif jump_state == JumpState.DOWN:
		#if is_on_floor():  
			## 落到地面
			#jump_state = JumpState.Land
		#else:
			## 下落过程中
			#velocity += get_gravity()
	#elif jump_state == JumpState.Land:
		#travel_to_state("Jump_Land")
		#jump_state = JumpState.Idle

# 也可使用rotate_x, rotate_y, rotate_z等方法
func rotate_look(node3d: Node3D, relative: Vector2) -> void:
	# 上下旋转, 限制上下旋转的角度
	node3d.rotation_degrees.x -= relative.y * LOOK_SPEED
	node3d.rotation_degrees.x = clamp(node3d.rotation_degrees.x, -45, 30)

	# 左右旋转，环绕
	node3d.rotation_degrees.y -= relative.x * LOOK_SPEED
	node3d.rotation_degrees.y = wrapf(node3d.rotation_degrees.y, 0, 360.0)

func travel_to_state(state: String) -> void:
	var playback = animation_tree.get(CURRENT_STATE_PLAYBACK_PATH)
	playback.travel(state)
	print(state)

func begin_jump() -> void:
	travel_to_state("Jump_Start")
	# 最好的效果是Jump_Start执行几帧后，才触发jump_queued=true
	# 但导入的AnimationPlayer是只读的，不能附加Track, 目前利用_on_animation_tree_animation_finished信号实现

func do_dodge() -> void:
	is_dodging = true
	var dir = Input.get_vector("move_left", "move_right", "move_up", "move_right")
	if movement_mode == MovementMode.UNFOCUSED:
		if dir == Vector2.ZERO:
			velocity = rig.transform.basis.z * DODGE_VELOCITY
			travel_to_state("Dodge_Backward")
		else:
			velocity = -rig.transform.basis.z * DODGE_VELOCITY
			travel_to_state("Dodge_Forward")
	else: 
		if dir == Vector2.ZERO:
			velocity = rig.transform.basis.z * DODGE_VELOCITY
			travel_to_state("Dodge_Backward")
		elif dir == Vector2.LEFT:
			velocity = -rig.transform.basis.x * DODGE_VELOCITY
			travel_to_state("Dodge_Left")
		elif dir == Vector2.RIGHT:
			velocity = rig.transform.basis.x * DODGE_VELOCITY
			travel_to_state("Dodge_Right")
		elif dir == Vector2.DOWN:
			travel_to_state("Dodge_Backward")
			velocity = rig.transform.basis.z * DODGE_VELOCITY
		elif dir == Vector2.UP:
			travel_to_state("Dodge_Forward")
			velocity = -rig.transform.basis.z * DODGE_VELOCITY

func _on_animation_tree_animation_finished(anim_name: StringName) -> void:
	if anim_name == "Jump_Start":
		jump_queued = true
	elif anim_name == "Dodge_Backward" or anim_name == "Dodge_Forward" or anim_name == "Dodge_Left" or anim_name == "Dodge_Right":
		is_dodging = false
	elif anim_name == "Interact":
		$InteractionHandler.clean_interactable()
